package cn.yangliu.nacos.oauth2.config;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.yangliu.nacos.oauth2.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.WebUtils;

/**
 * The interface Web security config.
 *
 * @author 问道于盲
 * @date 2019 -03-25
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * Custom AuthenticationSuccessHandler
     */
    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;

    /**
     * Custom AuthenticationFailureHandler
     */
    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;


    /**
     * current class instance's member.
     * The User service.
     */
    @Autowired
    private UserService userService;

    /**
     * current class instance's member.
     * The Password encoder.
     */
    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * INDEX_URI.
     */
    private static final String INDEX_URI = "/oauth/index";

    /**
     * Configure.
     *
     * @param auth the auth
     * @throws Exception the exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder);
    }

    /**
     * Configure.
     *
     * @param http the http
     * @throws Exception the exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/oauth/**")
                .authorizeRequests()
                .antMatchers(INDEX_URI,"/doc.html", "/webjars/**", "/swagger-resources/**",
                        "/v2/**", "/actuator/**", "/oauth/check_token", "/oauth/confirm_access",
                        "/oauth/error", "/oauth/custom_approval_page", "/oauth/custom_error_page",
                        "/oauth/goodbye","/oauth/css/**","/oauth/js/**","/oauth/img/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .csrf()
                .csrfTokenRepository(csrfTokenRepository())
                .and()
                .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
        http.formLogin().loginPage(INDEX_URI).loginProcessingUrl("/oauth/login").permitAll()
                .and().logout()
                .logoutUrl("/oauth/logout").permitAll().and().rememberMe();

        /**
         * login
         */
     /*   http.formLogin()
                .loginProcessingUrl(INDEX_URI)
                .successHandler(authenticationSuccessHandler)
                // 认证失败
                .failureHandler(authenticationFailureHandler)
                .permitAll();*/

    }

    /**
     * Configure {@link WebSecurity}. For example, if you wish to
     * ignore certain requests.
     *
     * @param web the web
     * @throws Exception the exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        /**
         * permit swagger
         */
        web.ignoring().antMatchers("/swagger-ui.html", "/swagger-resources/**", "/images/**", "/webjars/**",
                "/v2/api-docs", "/configuration/ui", "/configuration/security", "/favicon.ico", "/css/**","/js/**","/img/**","/oauth/check_token");
    }


    /**
     * Override AuthenticationManager.
     *
     * @return the authentication manager
     * @throws Exception the exception
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * Custom CSRF Header Filter.
     *
     * @return the filter
     */
    private Filter csrfHeaderFilter() {
        return new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain filterChain) throws ServletException, IOException {
                CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
                if (csrf != null) {
                    Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
                    String token = csrf.getToken();
                    if (cookie == null || token != null && !token.equals(cookie.getValue())) {
                        cookie = new Cookie("XSRF-TOKEN", token);
                        cookie.setPath("/");
                        response.addCookie(cookie);
                    }
                }
                filterChain.doFilter(request, response);
            }
        };
    }

    /**
     * Inject Cookie Csrf Token Repository.
     *
     * @return the csrf token repository
     */
    private CsrfTokenRepository csrfTokenRepository() {
        CookieCsrfTokenRepository repository = new CookieCsrfTokenRepository();
        repository.setHeaderName("X-XSRF-TOKEN");
        repository.setCookiePath("/");
        repository.setCookieHttpOnly(false);
        return repository;
    }


}